package com.gitee.wsl.compose.ext.ui


import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlin.math.absoluteValue
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.setValue


internal const val MAX_VELOCITY = 2500
internal const val SNAP_THRESHOLD = 0.2f

 enum class CollapsibleAreaValue {
    Collapsed, Expanded, Neutral
}

enum class ScrollDirection {
    Up, Down, None
}

@Stable
val ScrollState.scrollDirection: State<ScrollDirection>
    @Composable get() {
        val scrollDirection = remember { mutableStateOf(ScrollDirection.None) }
        LaunchedEffect(Unit) {
            launch {
                var lastScrollPosition = value
                snapshotFlow { value }.collectLatest {
                    scrollDirection.value = if (it > lastScrollPosition) {
                        ScrollDirection.Down
                    } else if (it < lastScrollPosition) {
                        ScrollDirection.Up
                    } else ScrollDirection.None
                    lastScrollPosition = it
                }
            }
        }
        return scrollDirection
    }



@Stable
val LazyListState.scrollDirection: State<ScrollDirection>
    @Composable get() {
        val scrollDirection = remember { mutableStateOf(ScrollDirection.None) }
        LaunchedEffect(Unit) {
            launch {
                var lastScrollPosition = firstVisibleItemScrollOffset.absoluteValue
                snapshotFlow { firstVisibleItemScrollOffset.absoluteValue }.collectLatest {
                    scrollDirection.value = if (it > lastScrollPosition) {
                        ScrollDirection.Down
                    } else if (it < lastScrollPosition) {
                        ScrollDirection.Up
                    } else ScrollDirection.None
                    lastScrollPosition = it
                }
            }
        }
        return scrollDirection
    }

internal val ScrollSnapSpec: AnimationSpec<Float> = spring(
    dampingRatio = Spring.DampingRatioNoBouncy,
    stiffness = 200f
)

/**
 * Remember the current offset of a [LazyListState].
 * Source:
 * https://stackoverflow.com/a/75677163
 * https://stackoverflow.com/a/70558512
 */
@Composable
fun rememberCurrentOffset(state: LazyListState): State<Int> {
    val position = remember { derivedStateOf { state.firstVisibleItemIndex } }
    val itemOffset = remember { derivedStateOf { state.firstVisibleItemScrollOffset } }
    val lastPosition = rememberPrevious(position.value)
    val lastItemOffset = rememberPrevious(itemOffset.value)
    val currentOffset = remember { mutableIntStateOf(0) }

    LaunchedEffect(position.value, itemOffset.value) {
        currentOffset.intValue = when {
            lastPosition == null || position.value == 0 -> itemOffset.value
            lastPosition == position.value -> currentOffset.intValue + (itemOffset.value - (lastItemOffset ?: 0))
            lastPosition > position.value -> currentOffset.intValue - (lastItemOffset ?: 0)
            else -> currentOffset.intValue + itemOffset.value
        }
    }

    return currentOffset
}

@Composable
fun <T> rememberPrevious(
    current: T,
    shouldUpdate: (prev: T?, curr: T) -> Boolean = { prev, curr -> prev != curr },
): T? {
    val ref = rememberRef<T>()
    SideEffect {
        if (shouldUpdate(ref.value, current)) {
            ref.value = current
        }
    }
    return ref.value
}

@Composable
fun <T> rememberRef(): MutableState<T?> {
    return remember {
        object : MutableState<T?> {
            override var value: T? = null
            override fun component1(): T? = value
            override fun component2(): (T?) -> Unit = { value = it }
        }
    }
}

// 如果滚动到了最下面 或者 不够长不能滚动时为true
val ScrollState.inBottomOrNotLarge: Boolean
    get() = value < maxValue || maxValue == 0

// 如果滚动到了最下面 或者 不够长不能滚动时为true
val LazyListState.inBottomOrNotLarge: Boolean
    get() = canScrollForward || firstVisibleItemIndex == 0 && firstVisibleItemScrollOffset == 0

@Composable
fun LazyListState.isScrollingUp(): Boolean {
    var previousIndex by remember(this) { mutableIntStateOf(firstVisibleItemIndex) }
    var previousScrollOffset by remember(this) { mutableIntStateOf(firstVisibleItemScrollOffset) }
    return remember(this) {
        derivedStateOf {
            if (previousIndex != firstVisibleItemIndex) {
                previousIndex > firstVisibleItemIndex
            } else {
                previousScrollOffset >= firstVisibleItemScrollOffset
            }.also {
                previousIndex = firstVisibleItemIndex
                previousScrollOffset = firstVisibleItemScrollOffset
            }
        }
    }.value
}

@Composable
fun LazyGridState.isScrollingUp(): Boolean {
    var previousIndex by remember(this) { mutableIntStateOf(firstVisibleItemIndex) }
    var previousScrollOffset by remember(this) { mutableIntStateOf(firstVisibleItemScrollOffset) }
    return remember(this) {
        derivedStateOf {
            if (previousIndex != firstVisibleItemIndex) {
                previousIndex > firstVisibleItemIndex
            } else {
                previousScrollOffset >= firstVisibleItemScrollOffset
            }.also {
                previousIndex = firstVisibleItemIndex
                previousScrollOffset = firstVisibleItemScrollOffset
            }
        }
    }.value
}

@Composable
fun ScrollState.isScrollingUp(): Boolean {
    var previousScrollOffset by remember(this) { mutableIntStateOf(value) }
    return remember(this) {
        derivedStateOf {
            (previousScrollOffset >= value).also {
                previousScrollOffset = value
            }
        }
    }.value
}